/*
 * Copyright (c) 2003-2010, Live Media Pty. Ltd.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice, this list of
 *     conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice, this list of
 *     conditions and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *  3. Neither the name of Live Media nor the names of its contributors may be used to endorse
 *     or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.colony.concurrent;

/**
 *  An Event Semaphore which allows a thread to waitSem() until post() has been called.
 *  If an EventSem has been posted it must be reset before waitSem() can be called again.
 */

public class EventSem
{

   private boolean _set;
   private long _postCount;   
   private EventSem[] _cascades;
   private int _cascadeCount;

   public EventSem()
   {
      _set = true;
      _postCount = 0;
      _cascades = new EventSem[50];
      _cascadeCount = 0;
   }

   /**
    * trigger the waiting threads to wake up.
    */

   public void post()
   {
      synchronized ( this )
      {
         if ( _set == true )
            _set = false;
         notifyAll();
 
         _postCount++;

         if ( _cascadeCount > 0 )
         {
            for (int x = 0 ; x<_cascadeCount ; x++ )
            {
               _cascades[ x ].post();
            }
         }         
      }
   }

   /**
    * wait for semaphore with a timeout
    */

   public void waitSem( long timeout )
   {
      synchronized( this )
      {
         if ( _set == false ) 
         {
	    return;
         }
         try
         {
           if ( timeout > 0 )
             wait( timeout );
           else
             wait();
         }
         catch( InterruptedException ex )
         {
            System.out.println( "WaitSem timeout: " + ex.toString() );
         }
      }
   }

   /**
    *  wait for semaphore without a timeout
    */

   public void waitSem()
   {
      synchronized( this )
      {
         if ( _set == false ) 
         {  
            return;
         }
         try
         {
            wait();
         }
         catch( InterruptedException ex )
         {
            System.out.println( "WaitSem timeout: " + ex.toString() );
         }
      }
   }

   /**
    *  Reset the semaphore to allow waitSem() to be called again.
    */

   public long reset()
   {
      long postCount;

      synchronized( this )
      {
         _set = true;
         postCount = _postCount;
         _postCount= 0;
      }

      return postCount;
   }

   public long query()
   {
      synchronized( this )
      {
         return _postCount;
      }
   }

   /**
    *  addCascade is used by MuxSem to allow a post on this sem to cause a post
    *  on additional sems aswell.
    */   

   protected void addCascade( EventSem sem )
   {
      synchronized( this )
      {
         _cascades[ _cascadeCount ] = sem;
         _cascadeCount++;
      }
   }

   /**
    *  removeSem is used by MuxSem to remove a semaphore which would be posted
    *  when this is posted.
    */

   protected void removeCascade( EventSem sem )
   {
      int y = 0;
      int x = 0;

      synchronized( this )
      {
         for( x=0 ; x< _cascadeCount ; x++ ) {
            // If we find the handle, don't increment y, so that next time round
            // it gets clobbered by the handles right of it.
            if ( _cascades[x] != sem ) {
               _cascades[y] = _cascades[x];
               y++;
            } 
         }
         // If sem has been removed, then y should not equal x.
         if ( x != y ) _cascadeCount--;   
      }
   }
}